1. Les méthodes

Les méthodes sont des concepts de programmations communs à de très nombreux langages de programmation aussi connues sous le nom de fonctions. C'est un peu comme en mathématiques quand on déclare f(x) = x² : on déclare une fonction dont le nom est f, qui prend un paramètre x et qui renvoie la valeur . Si j'appelle la fonction f avec le paramètre 5 je vais obtenir le résultat 25 : f(5) = 25 !

Sauf qu'ici, on va pouvoir créer des méthodes avec des noms plus explicites, des noms de variables plus élaborés, et des comportements plus complexes.

En Java, une méthode est définie par sa signature. C'est à dire que la fonction à un nom, un type de retour, des paramètres (optionnel). Si je veux par exemple définir une fonction équivalente à notre bonne vieille f(x) = x² en Java, je peux l'écrire :

long square(int value) {
  return value * value;
}

C'est quasiment la même chose ! Sauf que maintenant ma méthode est nommée square, on sait qu'elle renvoie un long et prend en paramètre un int value ​.

Je peux l'appeler ainsi :

long result = square(5);
IO.println(result); // 25

Créer ses propres méthodes

En Java, il est possible de créer ses propres méthodes, comme je l'ai fait avec square au dessus.
Pour cela, il vous suffit de lui donner un type de retour (ou void s'il n'y en a pas), un nom et des paramètres.
Ensuite, le corps de la méthode peut contenir du code comme on l'a toujours fait jusque là !

Tenez, par exemple on peut faire une méthode qui renvoie un boolean pour savoir si un nombre est pair :

boolean isOddNumber(int n) {
  if (n % 2 == 0) { // je vous avais dit que le modulo serait utile !
    return true;
  } else {
    return false;
  }
}

Et là, les super-malins pourraient remarquer que l'expression n % 2 == 0 renvoie déjà un boolean, et que le if pourrait être simplifié :

boolean isOddNumber(int n) {
  return n % 2 == 0; // ça marche aussi
}

Le mot clé return

Ça ne vous a probablement pas échappé, un nouveau mot clé à fait son apparition : return. C'est un mot clé utilisé dans les méthodes pour définir la valeur que la méthode va retourner (son résultat).
Une méthode ne renverra une valeur que si elle a un type de retour différent de void (qui signifie l'absence de type). En l'occurence, dans les deux exemples précédents, ma méthode long square(int value) a un type de retour long donc elle retourne une valeur de type long. Ma méthode boolean isOddNumber(int n) retourne un booléen avec return n % 2 == 0 qui renvoie soit true, soit false.

Tout code écrit après un return ne sera pas exécuté puisque ce mot clé permet d'arrêter l'exécution de la méthode pour renvoyer immédiatement son résultat.

boolean isOddNumber(int n) {
  if (n % 2 == 0) {
    return true; // fin de la méthode
    IO.println("Coucou !"); // ne sera jamais exécuté
  } else {
    return false; // fin de la méthode
    IO.println("Coucou !"); // ne sera jamais exécuté
  }
}

Dans ma méthode isOddNumber, j'ai deux fins alternatives : soit le nombre est pair, soit il ne l'est pas. Suivant quelle branche du if est exécutée, je renverrai true ou false mais je ne verrai jamais Coucou ! s'afficher dans la console !
Pour éviter les faux espoirs, le compilateur Java ne vous permettra pas d'écrire ça, il affichera une erreur Unreachable statement pour dire que l'instruction est impossible à atteindre.

Dans une méthode avec un type de retour void, ce mot clé peut être utilisé sans expliciter de valeur, dans le but de quitter la méthode. Il est implicitement présent à la fin de chaque méthode mais vous pouvez le rajouter vous même si vous le souhaitez !
Ça pourra être utile dans certains cas comme par exemple :

void drinkAlcohol(int age) {
  if (age < 18) {
    IO.println("Cannot dring before being 18!");
    return; // on interromp l'exécution
  }
  
  IO.println("Stay safe!");
}

Dans cet exemple, le fait d'interrompre l'exécution revient au même que d'écrire :

void drinkAlcohol(int age) {
  if (age < 18) {
    IO.println("Cannot dring before being 18!");
  } else {
    IO.println("Stay safe!");
  }
}

La méthode main

Eh oui ! Si on regarde attentivement, notre fameux main est une méthode ! Mais si ! Regardez :

void main() { }

C'est une méthode qui ne prend pas de paramètres, et renvoie void. C'est à dire qu'elle ne renvoie rien du tout ! Ce n'est d'ailleurs pas la seule méthode que vous utilisiez ! C'est aussi le cas de println().
Le cas de main est un peu spécial puisque comme évoqué dans la partie explications sur le Hello World, il est le point d'entrée du programme. Sans lui, pas d'exécution du programme !

Et pour boucler la boucle, on peut même relier toutes ces informations avec le concept précédemment abordé des expressions et instructions. Car si une méthode renvoie void, c'est une instruction, sinon, c'est une expression ! OMG TOUT EST LIÉ ! (oui)

La portée des variables

Les variables que vous déclarez dans une méthode ne sont visible que dans cette méthode. On dit qu'elle sont locales à la méthode.

void main() {
  String texte = "Coucou !";
  IO.println(texte);
  afficher();
}

void afficher() {
  IO.println(texte); // ça compile pas !
}

Ici, le programme ne compilera pas car la variable texte utilisée dans la fonction afficher() n'existe pas. Elle est déclarée dans la méthode main, et les variables déclarées dans une méthode ne sont visibles que depuis la méthode en question.

Si je veux utiliser ma méthode afficher pour afficher des variables de mon main, il faut que je lui passe en paramètre :

void main() {
  String texte = "Coucou !";
  IO.println(texte); // affiche bien Coucou !
  afficher(texte); // affiche bien Coucou !
}

void afficher(String str) { // maintenant la méthode a un paramètre qui s'appelle str
  IO.println(str);
}

Je rajoute un paramètre entre les parenthèses de la méthode afficher.

Ainsi, on réalise que les paramètres d'une méthode peuvent avoir n'importe quel nom, ils sont perçu comme une variable dont la valeur est attribuée par celui qui appelle la méthode. En l'occurence ici, c'est le main qui appelle afficher avec la variable texte en paramètre.

Cette notion de portée des variables est applicable de façon générale à n'importe quel bloc de code. Il faut que la variable soit déclarée pour être utilisée, et il faut qu'elle soit utilisée dans l'enceinte de sa portée :

void main() {
  int a = 5;

  for (int i = 0; i < 10; i++) {
    IO.println(a);
    a = a + i;
  }
  
  IO.println(a);
  IO.println(i); // compile pas car la variable i n'est visible que dans la boucle for
}

Si on veut absolument rendre ce code possible, il faut que la variable i soit déclarée en dehors de la boucle for, à la hauteur du main, exactement comme la variable a :

void main() {
  int a = 5;
  int i = 0;

  for (; i < 10; i++) { // ici je n'ai pas besoin de redéclarer i dans les parenthèses
    IO.println(a);
    a = a + i;
  }
  
  IO.println(a);
  IO.println(i); // compile !
}

Les méthodes utiles au début

Pour l'instant, vous ne faites qu'utiliser IO.println(), qui est une méthode fournie par le JDK mais c'est loin d'être la seule méthode disponible, et certaines seront utiles pour plus tard alors notons les ici pour pouvoir y revenir si nécessaire :

  • void IO.println() - affiche une nouvelle ligne dans la console
  • void IO.println(Object obj) - affiche le paramètre donné dans la console puis saute une ligne
  • void IO.print(Object obj) - affiche le paramètre donné dans la console sans sauter de lignes
  • String IO.readln() - lis une ligne entrée par utilisateur sur la console
  • String IO.readln(String prompt) - affiche le paramètre donné dans la console puis lis une ligne entrée par utilisateur sur la console
void main() {
  String name = IO.readln("Entrez votre nom : ");
  String age = IO.readln("Entrez votre age : ");

  IO.println("Bonjour " + name + ". Tu as " + age + " ans !");
}

Pasted image 20250421101825.png

Bonus

Comme vous l'avez peut-être remarqué, dans mon exemple juste au dessus, je demande à l'utilisateur d'entrer son age, et il est considéré comme String dans le programme. Ce n'est pas une bonne idée car si j'ai besoin de faire des calculs avec cette valeur, j'ai besoin que ce soit un int... Alors je vous donne une dernière méthode pratique pour palier à ce genre de soucis à l'avenir : Integer.parseInt(String s), qui permet de transformer la String s ​ donnée en paramètre en int !
Il faut faire attention à ne pas lui donner n'importe quoi par contre, sinon le programme va planter :

int a = Integer.parseInt("35"); // renvoie un int 35
int b = Integer.parseInt("Coucou !") // apocalypse

En cas d'apocalypse, le programme va vous afficher un truc du genre :
Exception in thread "main" java.lang.NumberFormatException: For input string: "Coucou !", qu'il faut comprendre comme un message d'insulte pour ne pas lui avoir donné une valeur qu'il peut convertir.

Ranger son code

Parfois quand notre programme devient complexe, il convient de le découper en différentes méthodes pour simplifier la lecture et la maintenabilité du programme.

Par exemple, imaginons un jeu quelconque. Les règles sont complexes, le jeu est compliqué à coder et il serait de la folie de coder tout le programme dans le main !
Au lieu de ça, le main pourrait ressembler à ça :

void main() {
  GameState gameState = initGameState(); // initialise la partie
  
  boolean gameOver = false;
  
  while(!gameOver) { // tant que la partie n'est pas finie
    play(gameState); // on joue
    gamerOver = checkCheckOver(gameState); // on vérifie si la partie est finie après ce tour
  }
}

avec des méthodes qui contiennent la complexité du code à côté :

GameState initGameState() {
  // ...
}

void play(GameState gameState) {
  // ...
}

boolean checkGameOver(GameState gameState) {
  // ...
}

On peut imaginer que ce jeu contienne de nombreuses données complexes, comme par exemple la liste des joueurs s'il est multijoueur, ou l'échiquier d'un jeu d'échecs.

Dans ces cas là, il est essentiel de savoir manipuler des objets. On rentre dans le vrai morceau ! YEEEHAAAA !